#pragma once
#ifndef RGSVERTEXTOOL_H
#define RGSVERTEXTOOL_H
#include<QtMath>
#include <qgsmaptooladvanceddigitizing.h>
#include <qgsrubberband.h>
#include<qobjectuniqueptr.h>
#include<qgspointlocator.h>
#include<qgsvertexmarker.h>

class QgsRubberBand;


struct Vertex
{
    Vertex( QgsVectorLayer *layer, QgsFeatureId fid, int vertexId )
        : layer( layer )
        , fid( fid )
        , vertexId( vertexId ) {}

    // TODO c++20 - replace with = default
    bool operator==( const Vertex &other ) const
    {
        return layer == other.layer && fid == other.fid && vertexId == other.vertexId;
    }
    bool operator!=( const Vertex &other ) const
    {
        return !operator==( other );
    }

    QgsVectorLayer *layer = nullptr;
    QgsFeatureId fid;
    int vertexId;
};


class QgsLockedFeature:public QObject
{


public:
    QgsLockedFeature(QgsFeatureId id, QgsVectorLayer *layer, QgsMapCanvas *canvas)
        :mlayer(layer),mFeatureId(id),mCanvas(canvas)
    {

    }
    QgsFeatureId featureId(){
        return mFeatureId;
    }
    QgsVectorLayer* layer(){
        return mlayer;
    }
    QgsVectorLayer* mlayer;
    QgsFeatureId mFeatureId;
    QgsMapCanvas *mCanvas = nullptr;
};

class RgsVertexTool:public QgsMapToolAdvancedDigitizing
{
public:

    enum VertexToolMode
    {
        ActiveLayer,
        AllLayers
    };

    enum DraggingVertexType
    {
        NotDragging,
        MovingVertex,
        AddingVertex,
        AddingEndpoint,
    };
    Q_ENUM( VertexToolMode )
    RgsVertexTool( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDock, VertexToolMode mode = RgsVertexTool::AllLayers );
    ~RgsVertexTool() override;

    //void cadCanvasPressEvent( QgsMapMouseEvent *e ) override;

    void cadCanvasReleaseEvent( QgsMapMouseEvent *e ) override;

    void cadCanvasMoveEvent( QgsMapMouseEvent *e ) override;

    void activate() override;

    void rangeMethodMoveEvent( QgsMapMouseEvent *e );
    void mouseMoveNotDragging( QgsMapMouseEvent *e );
    QgsPointLocator::Match snapToEditableLayer( QgsMapMouseEvent *e );
    void updateVertexBand( const QgsPointLocator::Match &m );
    QgsPointLocator::Match snapToPolygonInterior( QgsMapMouseEvent *e );
    void updateFeatureBand( const QgsPointLocator::Match &m );
    QgsGeometry cachedGeometry( const QgsVectorLayer *layer, QgsFeatureId fid );
    static QgsGeometry geometryToMultiPoint( const QgsGeometry &geom );
    static bool isCircularVertex( const QgsGeometry &geom, int vertexIndex );

    void startDragging( QgsMapMouseEvent *e );
    void startDraggingMoveVertex( const QgsPointLocator::Match &m );
    void setHighlightedVerticesVisible(bool visiable);
    void buildDragBandsForVertices( const QSet<Vertex> &movingVertices, const QgsPointXY &dragVertexMapPoint );
    void addDragCircularBand( QgsVectorLayer *layer, QgsPointXY v0, QgsPointXY v1, QgsPointXY v2, bool moving0, bool moving1, bool moving2, const QgsPointXY &mapPoint );
    void addDragStraightBand( QgsVectorLayer *layer, QgsPointXY v0, QgsPointXY v1, bool moving0, bool moving1, const QgsPointXY &mapPoint );
    void mouseMoveDraggingVertex( QgsMapMouseEvent *e );
private:

    struct LockedFeatureAlternatives
    {
        QPoint screenPoint;
        QList< QPair<QgsVectorLayer *, QgsFeatureId> > alternatives;
        int index = -1;
    };

    //! Keeps information about other possible features to select with right click. Null if no info is currently held.
    std::unique_ptr<LockedFeatureAlternatives> mLockedFeatureAlternatives;
    QObjectUniquePtr<QgsLockedFeature> mLockedFeature;
    std::unique_ptr<QgsPointLocator::Match> mLastSnap;

    std::unique_ptr<QgsPointLocator::Match> mNewVertexFromDoubleClick;

    std::unique_ptr<Vertex> mDraggingVertex;
    DraggingVertexType mDraggingVertexType = NotDragging;

    enum VertexSelectionMethod
    {
        SelectionNormal,   //!< Default selection: clicking vertex starts move, ctrl+click selects vertex, dragging rectangle select multiple vertices
        SelectionRange,    //!< Range selection: clicking selects start vertex, next click select final vertex, vertices in the range get selected
        SelectionPolygon,  //!< Polygon selection: alt+click starts digitizing a polygon, subsequent clicks add vertices, right click selects vertices within the polygon
    };

    //! Current vertex selection method
    VertexSelectionMethod mSelectionMethod = SelectionNormal;

    struct StraightBand
    {
        QgsRubberBand *band = nullptr;       //!< Pointer to the actual rubber band
        QgsPointXY p0, p1;                     //!< What are the original positions of points (in map units)
        bool moving0, moving1;               //!< Which points of the band are moving with mouse cursor
        QgsVector offset0, offset1;          //!< If the point is moving, what is the offset from the mouse cursor
    };

    struct CircularBand
    {
        QgsRubberBand *band = nullptr;        //!< Pointer to the actual rubber band
        QgsPointXY p0, p1, p2;                  //!< What are the original positions of points (in map units)
        bool moving0, moving1, moving2;       //!< Which points of the band are moving with mouse cursor
        QgsVector offset0, offset1, offset2;  //!< If the point is moving, what is the offset from the mouse cursor

        //! update geometry of the rubber band band on the current mouse cursor position (in map units)
        void updateRubberBand( const QgsPointXY &mapPoint );
    };
    QList<StraightBand> mDragStraightBands;
    QList<CircularBand> mDragCircularBands;

    QList<QgsVertexMarker *> mDragPointMarkers;
    QList<QgsVector> mDragPointMarkersOffset;


    QgsRubberBand* mFeatureBand;
    QgsRubberBand* mFeatureBandMarkers;
    QgsRubberBand* mVertexBand;
    VertexToolMode mMode;
    const QgsVectorLayer *mFeatureBandLayer = nullptr;
    QgsFeatureId mFeatureBandFid = 0;
    QHash<const QgsVectorLayer *, QHash<QgsFeatureId, QgsGeometry> > mCache;
};

#endif // RGSVERTEXTOOL_H
